#include "stdafx.h"
#include "Camera.h"
#include <cmath>
#include "DirectInput.h"
#include "Game.h"
#include "Player.h"

CCamera::CCamera() : m_position(D3DXVECTOR3(0.0f, 50.0f, 0.0f)),m_yaw(0),m_pitch(0),m_roll(0), m_bInvertY(false), m_bCursorLock(false)
{
	// Start with an orthagonal camera axis
	m_up=D3DXVECTOR3(0.0f,1.0f,0.0f);
	m_look=D3DXVECTOR3(0.0f,0.0f,1.0f);
	m_right=D3DXVECTOR3(1.0f,0.0f,0.0f);
}

CCamera::~CCamera(void)
{
}

CCamera* CCamera::GetInstance(void)
{
	static CCamera instance;
	return &instance;
}

void CCamera::CalculateViewMatrix(D3DXMATRIX *viewMatrix)
{
	/* Start with our camera axis pointing down z*/
	m_up=D3DXVECTOR3(0.0f,1.0f,0.0f);
	m_look=D3DXVECTOR3(0.0f,0.0f,1.0f);
	m_right=D3DXVECTOR3(1.0f,0.0f,0.0f);

	// Yaw is rotation around the y axis (m_up)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX yawMatrix;
	D3DXMatrixRotationAxis(&yawMatrix, &m_up, m_yaw);
	// To apply yaw we rotate the m_look & m_right vectors about the m_up vector (using our yaw matrix)
	D3DXVec3TransformCoord(&m_look, &m_look, &yawMatrix); 
	D3DXVec3TransformCoord(&m_right, &m_right, &yawMatrix); 

	// Pitch is rotation around the x axis (m_right)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX pitchMatrix;
	D3DXMatrixRotationAxis(&pitchMatrix, &m_right, m_pitch);
	// To apply pitch we rotate the m_look and m_up vectors about the m_right vector (using our pitch matrix)
	D3DXVec3TransformCoord(&m_look, &m_look, &pitchMatrix); 
	D3DXVec3TransformCoord(&m_up, &m_up, &pitchMatrix); 

	// Roll is rotation around the z axis (m_look)
	// Create a matrix that can carry out this rotation
	D3DXMATRIX rollMatrix;
	D3DXMatrixRotationAxis(&rollMatrix, &m_look, m_roll);
	// To apply roll we rotate up and right about the look vector (using our roll matrix)
	// Note: roll only really applies for things like aircraft unless you are implementing lean
	D3DXVec3TransformCoord(&m_right, &m_right, &rollMatrix); 
	D3DXVec3TransformCoord(&m_up, &m_up, &rollMatrix); 

	// Build the view matrix from the transformed camera axis
	D3DXMatrixIdentity(viewMatrix);

	viewMatrix->_11 = m_right.x; viewMatrix->_12 = m_up.x; viewMatrix->_13 = m_look.x;
	viewMatrix->_21 = m_right.y; viewMatrix->_22 = m_up.y; viewMatrix->_23 = m_look.y;
	viewMatrix->_31 = m_right.z; viewMatrix->_32 = m_up.z; viewMatrix->_33 = m_look.z;

	viewMatrix->_41 = - D3DXVec3Dot( &m_position,&m_right); 
	viewMatrix->_42 = - D3DXVec3Dot( &m_position,&m_up);
	viewMatrix->_43 = - D3DXVec3Dot( &m_position,&m_look);
}

// Yaw - rotation around y axis
void CCamera::Yaw(float amount) 
{
	m_yaw-=amount;
	m_yaw=RestrictAngleTo360Range(m_yaw);
}	

// Pitch - rotation around x axis
void CCamera::Pitch(float amount)
{
	m_pitch-=amount;
	if(m_pitch > D3DX_PI * 0.5)
		m_pitch = D3DX_PI * 0.5;

	if(m_pitch < -D3DX_PI * 0.5)
		m_pitch = -D3DX_PI * 0.5;
}

// Roll - rotation around z axis
// Note: normally only used for aircraft type cameras rather than land based ones
void CCamera::Roll(float amount) 
{
	m_roll+=amount;
	m_roll=RestrictAngleTo360Range(m_roll);
}

// Keep the angle in the range 0 to 360 (2*PI)
float CCamera::RestrictAngleTo360Range(float angle) const
{
	while(angle>2*D3DX_PI)
		angle-=2*D3DX_PI;

	while(angle<0)
		angle+=2*D3DX_PI;

	return angle;
}


void CCamera::ProcessCamera(float fTime)
{
	switch (m_nCameraMode)
	{
	case OPEN_WORLD_CAMERA:
		{
			OpenWorldCamera(fTime);
			break;
		}
	case COMBAT_CAMERA:
		{
			CombatCamera(fTime);
			break;
		}
	}
}

#define GROUND_CLAMP 15.0f
#define DISTANCE_MODIFIER 500.0f
void CCamera::CombatCamera(float fTime)
{
	//take player position go up then look
	
	if(1)
	{
		POINT mousePos;
		GetCursorPos( &mousePos );

		m_mouseDiff[0] = (float)(mousePos.x - m_LastMousePosition.x);
		m_mouseDiff[1] = (float)(mousePos.y - m_LastMousePosition.y);

		m_mouseDiff[0] *= fTime;
		m_mouseDiff[1] *= fTime;

		if(DirectInput::GetInstance()->MouseButtonDown(1))
		{
			if(m_bInvertY == true)
				Pitch(-m_mouseDiff[1]);
			else
				Pitch(m_mouseDiff[1]);

			Yaw(m_mouseDiff[0]);
			m_mouseDiff[0] = 0.0f;
			m_mouseDiff[1] = 0.0f;
		}

		SetCursorPos(512, 364);

		DirectInput* pInput = DirectInput::GetInstance();

		Player* pPlayer = Game::GetInstance()->GetPlayer();
		D3DXVECTOR3 newPos;

		if(pPlayer)
			newPos = Game::GetInstance()->GetPlayer()->GetPosition();

		//Enable Ground Clamping
		if(m_bGround_Clamp)
			newPos.y = GROUND_CLAMP;

		SetPosition(newPos);
	}

	D3DXMATRIX view;
	CalculateViewMatrix(&view);
	g_pD3DDevice->SetTransform(D3DTS_VIEW, &view);}

void CCamera::OpenWorldCamera(float fTime)
{

	static bool Pause = false;
	if(DirectInput::GetInstance()->KeyPressed(DIK_PERIOD))
		Pause = !Pause;
	if(Pause == false)
	{
		POINT mousePos;
		GetCursorPos( &mousePos );

		m_mouseDiff[0] = (float)(mousePos.x - m_LastMousePosition.x);
		m_mouseDiff[1] = (float)(mousePos.y - m_LastMousePosition.y);

		m_mouseDiff[0] *= fTime;
		m_mouseDiff[1] *= fTime;
		
		if(DirectInput::GetInstance()->MouseButtonDown(1))
		{
			if(m_bInvertY == true)
				Pitch(-m_mouseDiff[1]);
			else
				Pitch(m_mouseDiff[1]);

			Yaw(m_mouseDiff[0]);
			m_mouseDiff[0] = 0.0f;
			m_mouseDiff[1] = 0.0f;
		}

		SetCursorPos(512, 364);

		DirectInput* pInput = DirectInput::GetInstance();

		Player* pPlayer = Game::GetInstance()->GetPlayer();
		D3DXVECTOR3 newPos;
		
		if(pPlayer)
			newPos = Game::GetInstance()->GetPlayer()->GetPosition();

		//Enable Ground Clamping
		if(m_bGround_Clamp)
			newPos.y = GROUND_CLAMP;
				
		SetPosition(newPos);
	}

	D3DXMATRIX view;
	CalculateViewMatrix(&view);
	g_pD3DDevice->SetTransform(D3DTS_VIEW, &view);
}

void CCamera::Initialize(IDirect3DDevice9* pDevice, D3DXVECTOR3 startPosition, int nScreenWidth,int nScreenHeight, float fNearClip, float fFarClip)
{
	m_position = startPosition;
	m_bGround_Clamp = true;

	D3DXMatrixPerspectiveFovRH(&m_ProjectionMatrix, D3DX_PI/3,/*D3DXToRadian(45),*/(FLOAT)nScreenWidth / (FLOAT)nScreenHeight, fNearClip, fFarClip);

	pDevice->SetTransform(D3DTS_PROJECTION, &m_ProjectionMatrix);

	SetCursorPos(512, 364);
	GetCursorPos(&m_LastMousePosition);
}

void CCamera::TurnTo( D3DXMATRIX &CameraMatrix, const D3DXVECTOR3 &Target, float fTime )
{
	D3DXVECTOR3 CameraPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	CameraPosition.x = CameraMatrix._41;
	CameraPosition.y = CameraMatrix._42;
	CameraPosition.z = CameraMatrix._43;

	D3DXVECTOR3 VectorToPoint = CameraPosition - Target;
	D3DXVec3Normalize(&VectorToPoint, &VectorToPoint);

	D3DXVECTOR3 CameraAxisX = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	CameraAxisX.x = CameraMatrix._11;
	CameraAxisX.y = CameraMatrix._12;
	CameraAxisX.z = CameraMatrix._13;

	D3DXVECTOR3 CameraAxisY = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	CameraAxisY.x = CameraMatrix._21;
	CameraAxisY.y = CameraMatrix._22;
	CameraAxisY.z = CameraMatrix._23;

	//////////////////////////////////////////////////////////////////////
	// Rotation Prep
	D3DXMATRIX RotationMatrix = D3DXMATRIX(0.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 0.0f);
	D3DXMatrixIdentity(&RotationMatrix);

	float Sin = 0.0f;
	float Cos = 0.0f;
	//////////////////////////////////////////////////////////////////////

	if (D3DXVec3Dot(&CameraAxisX, &VectorToPoint) > 0.01f)
	{
		Sin = sinf(-fTime);
		Cos = cosf(-fTime);

		//mat.rotate_y_pre(-fTime);
		RotationMatrix._11 = Cos;
		RotationMatrix._13 = -Sin;
		RotationMatrix._31 = Sin;
		RotationMatrix._33 = Cos;

		CameraMatrix = RotationMatrix * CameraMatrix;
	}

	else if(D3DXVec3Dot(&CameraAxisX, &VectorToPoint) < -0.01f)
	{
		Sin = sinf(fTime);
		Cos = cosf(fTime);

		//mat.rotate_y_pre(fTime);
		RotationMatrix._11 = Cos;
		RotationMatrix._13 = -Sin;
		RotationMatrix._31 = Sin;
		RotationMatrix._33 = Cos;

		CameraMatrix = RotationMatrix * CameraMatrix;
	}

	if (D3DXVec3Dot(&CameraAxisY, &VectorToPoint) > 0.01f)
	{
		Sin = sinf(fTime);
		Cos = cosf(fTime);

		//mat.rotate_x_pre(fTime);
		RotationMatrix._22 = Cos;
		RotationMatrix._23 = Sin;
		RotationMatrix._32 = -Sin;
		RotationMatrix._33 = Cos;

		CameraMatrix = RotationMatrix * CameraMatrix;
	}

	else if(D3DXVec3Dot(&CameraAxisY, &VectorToPoint) > -0.01f)
	{
		Sin = sinf(-fTime);
		Cos = cosf(-fTime);

		//mat.rotate_x_pre(-fTime);
		RotationMatrix._22 = Cos;
		RotationMatrix._23 = Sin;
		RotationMatrix._32 = -Sin;
		RotationMatrix._33 = Cos;

		CameraMatrix = RotationMatrix * CameraMatrix;
	}

	D3DXVECTOR3 xAxis;
	D3DXVECTOR3 AxisZ = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	AxisZ.x = CameraMatrix._31;
	AxisZ.y = CameraMatrix._32;
	AxisZ.z = CameraMatrix._33;

	D3DXVec3Cross(&xAxis, &m_up, &AxisZ);
	D3DXVec3Normalize(&xAxis, &xAxis);

	D3DXVECTOR3 yAxis;
	D3DXVECTOR3 AxisY = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	AxisY.x = CameraMatrix._21;
	AxisY.y = CameraMatrix._22;
	AxisY.z = CameraMatrix._23;

	D3DXVec3Cross(&yAxis, &AxisZ, &xAxis);
	D3DXVec3Normalize(&yAxis, &yAxis);

	CameraMatrix._11 = xAxis.x;
	CameraMatrix._12 = xAxis.y;
	CameraMatrix._13 = xAxis.z;

	CameraMatrix._21 = yAxis.x;
	CameraMatrix._22 = yAxis.y;
	CameraMatrix._23 = yAxis.z;
}